#pragma once

#include "sfc/core/num.h"
#include "sfc/core/reflect.h"
#include "style.h"

namespace sfc::fmt {

template <class T>
struct Display {
  const T& _inn;

 public:
  void fmt(auto& f) const {
    if constexpr (requires() { _inn.fmt(f); }) {
      _inn.fmt(f);
    } else {
      auto imp = f.debug_struct();
      reflect_struct(_inn).fields().map([&](const auto& item) {  //
        imp.field(item.name, item.value);
      });
    }
  }
};

template <class T>
Display(const T&) -> Display<T>;

template <class T, usize N>
Display(const T (&)[N]) -> Display<T[N]>;

template <>
struct Display<char> {
  char _inn;

 public:
  void fmt(auto& f) const {
    f.pad(str::Str{&_inn, 1});
  }
};

template <>
struct Display<bool> {
  bool _inn;

 public:
  void fmt(auto& f) const {
    f.pad(_inn ? "true" : "false");
  }
};

template <trait::Int T>
struct Display<T> {
  T _inn;

 public:
  auto to_str(const Style& style, slice::Slice<char> buf) const -> str::Str;

  void fmt(auto& f) const {
    char buf[64 + 16];

    const auto s = this->to_str(f.style(), buf);
    f.pad_num(_inn < 0, s);
  }
};

template <trait::Float T>
struct Display<T> {
  T _inn;

 public:
  auto to_str(const Style& style, slice::Slice<char> buf) const -> str::Str;

  void fmt(auto& f) const {
    char buf[64 + 16];

    const auto s = this->to_str(f.style(), buf);
    f.pad_num(_inn < 0, s);
  }
};

template <class T, usize N>
struct Display<T[N]> {
  const T (&_inn)[N];

 public:
  void fmt(auto& f) const {
    if constexpr (__is_same(T, char)) {
      f.pad(_inn);
    } else {
      const auto v = slice::Slice<const T>{_inn};
      f.debug_list().entries(v.iter());
    }
  }
};

template <>
struct Display<void*> {
  const void* _inn;

 public:
  auto to_str(const Style& style, slice::Slice<char> buf) const -> str::Str;

  void fmt(auto& f) const {
    char buf[64 + 16];
    const auto s = this->to_str(f.style(), buf);
    f.pad_num(false, s);
  }
};

template <class T>
struct Display<T*> {
  const T* _inn;

 public:
  void fmt(auto& f) const {
    if constexpr (__is_same(const T, const char)) {
      f.pad(_inn);
    } else {
      char buf[64 + 16];
      const auto s = Display<void*>{_inn}.to_str(f.style(), buf);
      f.pad_num(false, s);
    }
  }
};

template <trait::Enum T>
struct Display<T> {
  T _inn;

 public:
  void fmt(auto& f) const {
    const auto name = reflect::enum_name(_inn);
    if (name != nullptr) {
      f.pad(name);
    } else {
      const auto int_val = static_cast<__underlying_type(T)>(_inn);
      f.write(int_val);
    }
  }
};

}  // namespace sfc::fmt
